namespace XTCast.Mp3 {
	using System;
	using System.IO;
	using XTCast.Mp3;
	using XTCast.Mp3.Encode;
	using XTCast.Mp3.Decode;
	using TerastarLib.Utils;
	
	public class MP3ReencodeFrameReader:IFrameReader,IDisposable {
		private MP3Frame frame=new MP3Frame();
		private bool readFrame = false;
		private Stream encode;
		private XTCast.Mp3.Decode.DecodeFilter decode;
		private IFrameReader inputReader;
		private FifoStream fifo;
		private FrameReadInfo i = new FrameReadInfo(32768,2000);
		private int bitrate, sampleRate, channels;

		public MP3ReencodeFrameReader(Stream inputStream, int bitrate, int channels, int sampleRate):this(new MP3FrameReader(inputStream), bitrate, channels, sampleRate) {}
		public MP3ReencodeFrameReader(IFrameReader inputReader, int bitrate, int channels, int sampleRate):base() {
			this.inputReader = inputReader;
			this.fifo = new FifoStream();
			this.bitrate = bitrate;
			this.channels = channels;
			this.sampleRate = sampleRate;
		}

		public void MoveData() {
			byte[] buf = inputReader.ReadFrames(i);
			if (encode == null && decode == null) {
				XTCast.Wave.WaveFormat wf = new XTCast.Wave.WaveFormat(inputReader.SampleRate,16,channels); 
				BE_CONFIG be = new BE_CONFIG(wf,bitrate/1000,sampleRate);
				encode = new EncodeFilter(fifo,wf,be);
				encode = new PCM.PCMRemoveSilence(encode,wf);
				if (inputReader.Channels==channels)
					decode = new DecodeFilter(encode);
				else {
					if (inputReader.Channels==2 && channels==1) 
						decode = new DecodeFilter(new PCM.PCMFilterStream(encode,PCM.PCMFilterStream.Operations.StereoToMono));
					else if (inputReader.Channels==1 && channels==2) 
						decode = new DecodeFilter(new PCM.PCMFilterStream(encode,PCM.PCMFilterStream.Operations.MonoToStereo));		
					else throw new Exception(String.Format("Cannot convert {0} channels to {1} channels",inputReader.Channels,channels));
				}
			}
			decode.Write(buf,0,i.ActualLength);
		}

		private bool ReadFrame() {
			while (!readFrame) {
				if (frame.ReadFrame(fifo))
					readFrame = !frame.IsVbrFrame;
				else 
					return false;
			}
			return true;
		}

		public virtual byte[] ReadFrames(FrameReadInfo info) {
			byte[] b = new byte[info.RequestLength];
			info.ActualLength=0;
			info.ActualFrames=0;
			info.EOF=false;

			while (info.ActualMillis < info.RequestMillis) {
				if (fifo.Length<info.RequestLength)
					MoveData();
				if (!ReadFrame()) {
					info.EOF = true;
					return b;
				}
				if (info.ActualLength+frame.Size > info.RequestLength )
					return b;
				readFrame = false;
				Array.Copy(frame.Frame, 0, b, info.ActualLength, frame.Size);
				info.ActualLength += frame.Size; info.ActualFrames++;
			}
			return b;
		}

		public virtual void SkipFrames(FrameReadInfo info) {
			info.ActualLength=0;
			info.ActualFrames=0;
			info.EOF=false;

			inputReader.SkipFrames(info);
/*
			while (info.ActualMillis < info.RequestMillis) {
				if (fifo.Length<info.RequestLength)
					MoveData();
				if (!ReadFrame()) {
					info.EOF = true;
					return;
				}
				if (info.ActualLength+frame.Size > info.RequestLength )
					return;
				readFrame = false;
				info.ActualLength += frame.Size; info.ActualFrames++;
			}
			return;
			*/
		}

		public void Dispose() {
			Close();
		}

		public void Close() {
			if (encode!=null)
				encode.Close();
			if (decode!=null)
				decode.Close();
		}
	
		public int Channels {
			get {
				return channels;
			}
		}
	
		public int SampleRate {
			get {
				return sampleRate;
			}
		}

		public int AvgBitrate {
			get {
				return bitrate;
			}
		}

		public void Reset() {
			inputReader.Reset();
		}
	}
}